# 定义费用策略接口
import datetime
import decimal
from decimal import Decimal, ROUND_HALF_UP
from typing import List

from sqlalchemy.orm import Session

from module_admin.dao.cms_car_dao import CmsCarDao
from module_admin.dao.cms_expenses_dao import CmsExpenseDao
from module_admin.dao.cms_order_execute_dao import CmsOrderExecuteDao
from module_admin.dao.dict_dao import DictDataDao
from module_admin.entity.do.cms_car import CmsCar
from module_admin.entity.do.cms_expense import CmsExpense
from module_admin.entity.do.cms_order_execute import CmsOrderExecute
from module_admin.entity.do.dict_do import SysDictData
from module_admin.entity.vo.cms_driver_vo import CrudDriverResponse
from module_admin.entity.vo.cms_order_vo import CrudOrderResponse
from module_admin.entity.vo.expense_vo import ExpenseDate, ExpensesModel, ExpensePageObject, ExpensesModelForPage, \
    ExpensesModelForJisuan


def split_by_0400(start_time: datetime.datetime, end_time: datetime.datetime):
    if isinstance(start_time, str):
        start_time = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
    if isinstance(end_time, str):
        end_time = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
    segments = []
    hour_ = start_time.hour > 4
    while start_time < end_time:
        expense_date = ExpenseDate()
        expense_date.start_time = start_time
        day_start = start_time.replace(hour=4, minute=0, second=0, microsecond=0)
        if hour_:
            day_start = day_start + datetime.timedelta(days=1)
        if end_time > day_start:
            expense_date.end_time = start_time.replace(hour=4, minute=0, second=0, microsecond=0)
            segments.append(expense_date)
        else:
            expense_date.end_time = end_time
            segments.append(expense_date)
        start_time = start_time + datetime.timedelta(days=1)
    return segments


class ExpensesStrategy():
    # 定义促销活动
    def get_price(self, expense_shezhi: CmsExpense,
                  mileage: decimal,
                  expense_date: ExpenseDate,
                  system_online_time: datetime.datetime,
                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        return ExpensesModelForJisuan()


# 定义具体费用策略
class Zero2Thirty(ExpensesStrategy):
    def get_price(self, expense_shezhi: CmsExpense,
                  mileage: decimal,
                  expense_date: ExpenseDate,
                  system_online_time: datetime.datetime,
                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        jisuan = ExpensesModelForJisuan()
        if system_online_time:
            seconds = (expense_date.end_time - system_online_time).seconds
        else:
            seconds = (expense_date.end_time - expense_date.start_time).seconds
        if seconds <= 2 * 3600:
            jisuan.starting_price_sum = expense_shezhi.starting_price
        elif seconds <= 6 * 3600:
            jisuan.day_price_sum = expense_shezhi.day_price / Decimal("2")
        elif seconds <= 8 * 3600:
            jisuan.day_price_sum = expense_shezhi.day_price / Decimal("2")
        elif is_first_or_last:
            jisuan.day_price_sum = expense_shezhi.day_price * Decimal("1.5")
        else:
            jisuan.day_price_sum = expense_shezhi.day_price
        return jisuan


class Thirty2Sixty(ExpensesStrategy):
    def get_price(self, expense_shezhi: CmsExpense,
                  mileage: decimal,
                  expense_date: ExpenseDate,
                  system_online_time: datetime.datetime,
                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        jisuan = ExpensesModelForJisuan()
        if system_online_time:
            seconds = (expense_date.end_time - system_online_time).seconds
        else:
            seconds = (expense_date.end_time - expense_date.start_time).seconds
        if seconds <= 2 * 3600:
            jisuan.starting_price_sum = expense_shezhi.starting_price
        elif seconds <= 6 * 3600:
            jisuan.day_price_sum = expense_shezhi.day_price / Decimal("2")
        elif seconds <= 8 * 3600:
            jisuan.day_price_sum = expense_shezhi.day_price
        elif is_first_or_last:
            jisuan.day_price_sum = expense_shezhi.day_price * Decimal("1.5")
        else:
            jisuan.day_price_sum = expense_shezhi.day_price
        return jisuan


class Sixty2HundredTwenty(ExpensesStrategy):
    def get_price(self, expense_shezhi: CmsExpense,
                  mileage: decimal,
                  expense_date: ExpenseDate,
                  system_online_time: datetime.datetime,
                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        jisuan = ExpensesModelForJisuan()
        if system_online_time:
            seconds = (expense_date.end_time - system_online_time).seconds
        else:
            seconds = (expense_date.end_time - expense_date.start_time).seconds
        if seconds <= 6 * 3600:
            result = expense_shezhi.miles_price * mileage
            jisuan.miles_price_sum = result.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        elif seconds <= 8 * 3600:
            jisuan.day_price_sum = expense_shezhi.day_price
        elif is_first_or_last:
            jisuan.day_price_sum = expense_shezhi.day_price * Decimal("1.5")
        else:
            jisuan.day_price_sum = expense_shezhi.day_price
        return jisuan


class HundredTwenty2Endless(ExpensesStrategy):
    def get_price(self, expense_shezhi: CmsExpense,
                  mileage: decimal,
                  expense_date: ExpenseDate,
                  system_online_time: datetime.datetime,
                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        jisuan = ExpensesModelForJisuan()
        if system_online_time:
            seconds = (expense_date.end_time - system_online_time).seconds
        else:
            seconds = (expense_date.end_time - expense_date.start_time).seconds
        if seconds <= 8 * 3600:
            result = expense_shezhi.miles_price * mileage
            jisuan.miles_price_sum = result.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        elif is_first_or_last:
            result = expense_shezhi.miles_price * mileage
            jisuan.miles_price_sum = result.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        else:
            result = expense_shezhi.miles_price * mileage
            mi = result.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
            da = expense_shezhi.day_price
            if mi < da:
                jisuan.miles_price_sum = mi
            else:
                jisuan.day_price_sum = da
        return jisuan


# 定义上下文类，负责调用不同的促销策略
class ExpensesContext:
    def __init__(self, expenses_strategy: ExpensesStrategy):
        self._expenses_strategy = expenses_strategy

    def execute_expenses_strategy(self, expense_shezhi: CmsExpense,
                                  mileage: decimal,
                                  expense_date: ExpenseDate,
                                  system_online_time: datetime.datetime,
                                  is_first_or_last: bool) -> ExpensesModelForJisuan:
        return self._expenses_strategy.get_price(expense_shezhi, mileage, expense_date, system_online_time,
                                                 is_first_or_last)


class ExpensesService:
    @classmethod
    def get_expenses(cls, db: Session,
                     order_id: str):
        # order_id查询车型
        order_execute: CmsOrderExecute = CmsOrderExecuteDao.get_one_by_order_id(db, order_id)
        if order_execute is None:
            return None
        car: CmsCar = CmsCarDao.get_car_by_car_id(db, order_execute.car_id)
        if car is None:
            return None
        expense_shezhi: CmsExpense = CmsExpenseDao.get_one_by_car_type(db, car.car_type, car.car_brand)
        if expense_shezhi is None:
            return None
        if order_execute.reality_begin_time is None or order_execute.reality_end_time is None:
            return None
        expense_date_list: List[ExpenseDate] = split_by_0400(order_execute.reality_begin_time,
                                                             order_execute.reality_end_time)
        expense_list = []
        i = 0
        for expense_date in expense_date_list:
            i = i + 1
            if order_execute.bill_type == "beidou":
                mileage = 30
            else:
                mileage = order_execute.sheet_mileage / len(expense_date_list)
            system_online_time = None
            if i == len(expense_date_list) and len(expense_date_list) > 1:
                system_online_time = expense_date.start_time
            if 0 <= mileage < 30:
                expenses_strategy = Zero2Thirty()
            elif 30 <= mileage < 60:
                expenses_strategy = Thirty2Sixty()
            elif 60 <= mileage < 120:
                expenses_strategy = Sixty2HundredTwenty()
            else:
                expenses_strategy = HundredTwenty2Endless()
            expenses_context = ExpensesContext(expenses_strategy)
            expenses: ExpensesModelForJisuan = expenses_context.execute_expenses_strategy(
                expense_shezhi,
                mileage,
                expense_date,
                system_online_time,
                i == 1 or i == len(expense_date_list))
            expense_list.append(expenses)
        return expense_list

    @classmethod
    def create_expense(cls, query_db: Session, expense_add: ExpensesModel):
        try:
            CmsExpenseDao.create_expense(query_db, expense_add)
            query_db.commit()
            result = dict(is_success=True, message='新增成功')
        except Exception as e:
            query_db.rollback()
            result = dict(is_success=False, message=str(e))

        return CrudOrderResponse(**result)

    @classmethod
    def page(cls, db: Session, query_object: ExpensePageObject):
        device_list_result = CmsExpenseDao.page(db, query_object)
        list0 = []
        for e in device_list_result:
            page_dict = e.__dict__
            model = ExpensesModelForPage(**page_dict)
            list0.append(model)
        return list0

    @classmethod
    def page(cls, db: Session, query_object: ExpensePageObject):
        device_list_result = CmsExpenseDao.page(db, query_object)
        cheliangleixing_list: List[SysDictData] = DictDataDao.query_dict_data_list(db, "cheliangleixing")
        list0 = []
        for e in device_list_result:
            page_dict = e.__dict__
            DictDataDao.chang_data_value_to_code_new(cheliangleixing_list, "car_type", page_dict["car_type"],
                                                     page_dict)
            model = ExpensesModelForPage(**page_dict)
            list0.append(model)
        return list0

    @classmethod
    def count(cls, db: Session, query_object: ExpensePageObject):
        return CmsExpenseDao.count(db, query_object)

    @classmethod
    def delete_expense(cls, query_db: Session, id: int):
        CmsExpenseDao.delete_expense_by_id(query_db, id)

    @classmethod
    def update_expense(cls, db: Session, expense_update: ExpensesModel):
        expense_update_dict = expense_update.dict(exclude_unset=True)
        try:
            CmsExpenseDao.update_expense_by_id(db, expense_update_dict)
            db.commit()
            result = dict(is_success=True, message='更新成功')
        except Exception as e:
            db.rollback()
            result = dict(is_success=False, message=str(e))
        return CrudDriverResponse(**result)
